#pragma once

#include "Message.hpp"
#include "Net.hpp"

namespace rpc
{
    class Callback
    {
    public:
        using ptr = std::shared_ptr<Callback>;
        virtual void onMessage(const BaseConnection::ptr &, const BaseMessage::ptr &msg) = 0;
    };

    template<class T>
    class CallbackT : public Callback
    {
    public:
        using ptr = std::shared_ptr<CallbackT<T>>;
        
        // server/client 注册消息类型回调函数: dispatcher->registerHandler<rpc::RpcRequest>(rpc::MSG_TYPE::REQ_RPC, onRpcRequest);
        // 因此, CallbackT<T> 中的 T 的 rpc::RpcRequest 这样的类型

        // using MessageCallback = std::function<void(const BaseConnection::ptr &, const T &msg)>; // error
        // MessageCallback 的第二个参数应该是 rpc::RpcRequest::ptr 而不是 rpc::RpcRequest
        
        using MessageCallback = std::function<void(const BaseConnection::ptr &, std::shared_ptr<T> &msg)>;

        CallbackT(const MessageCallback& callback) : _callback(callback) {}
        virtual void onMessage(const BaseConnection::ptr &conn, const BaseMessage::ptr &msg) override
        {   
            // _callback(conn, msg); // error
            // _callback 的第二个参数是 rpc::TopicRequest::ptr 这样的派生类指针, 不能直接将 msg 作为第二个参数传入 _callback
            auto type_msg = std::dynamic_pointer_cast<T>(msg); // T 就是 msg->type() 对应的消息类型
            _callback(conn, type_msg);
        }
    private:
        MessageCallback _callback; 
    };
    
    // using MessageCallback = std::function<void(const BaseConnection::ptr &, const BaseMessage::ptr &msg)>;
    class Dispatcher
    {
    public:
        using ptr = std::shared_ptr<Dispatcher>;

        template <class T>
        void registerHandler(const MSG_TYPE &type, const typename CallbackT<T>::MessageCallback handler)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            typename CallbackT<T>::ptr callback = std::make_shared<CallbackT<T>>(handler);

            _msg_handlers.insert(std::make_pair(type, callback));
        }

        // 把 Dispatcher::onMessage 绑定到 server/client 的setMessageCallback 中
        void onMessage(const BaseConnection::ptr &conn, const BaseMessage::ptr &msg)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            MSG_TYPE type = msg->type();
            auto it = _msg_handlers.find(type);
            if (it == _msg_handlers.end())
            {
                ELOG("unknown message type");
                return;
            }
            it->second->onMessage(conn, msg); 
        }

    private:
        std::mutex _mutex;
        std::unordered_map<MSG_TYPE, Callback::ptr> _msg_handlers;
        // 1. 
        // 封装 CallbackT 是为了在server/client中注册 消息类型及其对应的回调函数 时
        // 回调函数参数类型, 可以直接使用RpcRequest这样的派生类
        // 2. 
        // 如果将_msg_handlers 的第二个参数设为 CallbackT<T>::ptr, 仍然会导致实例化出不同的 Dispatcher 对象
        // 所以引入 Callback::ptr 这样的基类指针
    };
}